package engineer.seniordesign.cynogriffin;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Semaphore;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import android.androidVNC.VNC;
import android.app.ActionBar;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.Environment;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout.LayoutParams;

/**
 * @author bweintra
 *
 */
public class AppViewActivity extends Activity {
	private HashMap<int[], MyImageButton> elements; // Store GUI elements
	private String host, username, password, appName, xml;
	private int port;
	private SharedPreferences settings;
	private VNC vnc;
	private boolean stopThreads = false;
	private Semaphore mutexVNC; // A mutex lock for the VNC thread
	private Bitmap master; // A master copy of the desktop screen shot
	private int index; // An index to keep track of the GUI elements

	// XML attribute labels
	private final String KEY_BUTTON 		= "button";
	private final String KEY_EDIT_TEXT 		= "edittext";
	private final String KEY_WINDOW 		= "window";
	private final String KEY_ID 			= "id";
	private final String KEY_MOBILE_X 		= "mleft";
	private final String KEY_MOBILE_Y 		= "mtop";
	private final String KEY_MOBILE_WIDTH 	= "mwidth";
	private final String KEY_MOBILE_HEIGHT 	= "mheight";
	private final String KEY_SERVER_X 		= "dleft";
	private final String KEY_SERVER_Y 		= "dtop";
	private final String KEY_SERVER_WIDTH 	= "dwidth";
	private final String KEY_SERVER_HEIGHT 	= "dheight";
	private final String KEY_PAGE 			= "pagename";
	
	// GUI element types
	private final int BUTTON = 1;
	private final int EDITTEXT = 2;
	private final int WINDOW = 3;
	
	List<MyImageButton> ibs = new ArrayList<MyImageButton>();
	

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_app_view);

		// Action bar options if SDK is high enough
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
			ActionBar actionBar = getActionBar();
			actionBar.setDisplayHomeAsUpEnabled(true);
		}

		appName = "";
		Bundle extras = getIntent().getExtras();
		if (extras != null) {
			appName = extras.getString("AppName");
			settings = getSharedPreferences("settings", MODE_PRIVATE);
			host = settings.getString("prefHost", null);
			port = Integer.parseInt(settings.getString("prefPort", null));
			username = settings.getString("prefUsername", null);
			password = settings.getString("prefPassword", null);   
		}

		mutexVNC = new Semaphore(1);
		
		try {
			mutexVNC.acquire();
		} catch (InterruptedException e2) {
			e2.printStackTrace();
		}
		
    	VNCThread threadVNC = new VNCThread();
    	threadVNC.start();

		if (!appName.isEmpty()) {
			try {
				mutexVNC.acquire();
				
				BitmapLoop bl = new BitmapLoop();
				bl.start();
				
				elements = new HashMap<int[], MyImageButton>();
//				elements = new HashMap<int[], BitmapView>();

				XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
				factory.setNamespaceAware(true);
				XmlPullParser xpp = factory.newPullParser();
				xpp.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, false);

				xpp.setInput(new StringReader(xml));
				int eventType = xpp.getEventType();
				String name = "";
				mutexVNC.acquire();
				index = -1;
				while (true) {
					try {
						eventType = xpp.next();
					} catch (XmlPullParserException e1) {
						e1.printStackTrace();
					} 
					name = xpp.getName();
					
					if ("link".equals(name) && eventType == XmlPullParser.END_TAG) {
						break;
					}
					
					if("link".equals(name) && eventType != XmlPullParser.END_TAG) {
						final String launch = xpp.getAttributeValue(null, "application");
						Thread launcher = new Thread() {
							@Override
							public void run() {
								vnc.launchApplication(launch);
							}
						};
						launcher.start();
					}
					
					if(KEY_BUTTON.equals(name) && eventType != XmlPullParser.END_TAG) { 
						newElement(xpp, BUTTON, eventType);
					} else if(KEY_EDIT_TEXT.equals(name) && eventType != XmlPullParser.END_TAG) {
						newElement(xpp, EDITTEXT, eventType);
					} else if (KEY_WINDOW.equals(name) && eventType != XmlPullParser.END_TAG) { 
						newElement(xpp, WINDOW, eventType);
					}
				}

			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e1) {
				e1.printStackTrace();
			} catch (XmlPullParserException e1) {
				e1.printStackTrace();
			} catch (InterruptedException ie) {
				ie.printStackTrace();
			}

		}
	}
	
	/**
	 * @param xpp
	 * @param elementType
	 * @param eventType
	 */
	private void newElement(XmlPullParser xpp, int elementType, int eventType) {
		int id  	= Integer.parseInt(xpp.getAttributeValue(null, KEY_ID));
		int x 		= Integer.parseInt(xpp.getAttributeValue(null, KEY_MOBILE_X));
		int y		= Integer.parseInt(xpp.getAttributeValue(null, KEY_MOBILE_Y));
		int width 	= Integer.parseInt(xpp.getAttributeValue(null, KEY_MOBILE_WIDTH));
		int height  = Integer.parseInt(xpp.getAttributeValue(null, KEY_MOBILE_HEIGHT));
		int sx 		= Integer.parseInt(xpp.getAttributeValue(null, KEY_SERVER_X));
		int sy 		= Integer.parseInt(xpp.getAttributeValue(null, KEY_SERVER_Y));
		int sWidth 	= Integer.parseInt(xpp.getAttributeValue(null, KEY_SERVER_WIDTH));
		int sHeight = Integer.parseInt(xpp.getAttributeValue(null, KEY_SERVER_HEIGHT));
		String page = xpp.getAttributeValue(null, KEY_PAGE);

		int[] buttonVals = {x, y, width, height, sx, sy, sWidth, sHeight};
		
		Bitmap window = Bitmap.createBitmap(master, sx, sy, sWidth, sHeight);
		MyImageButton ib = new MyImageButton(this);
		ib.setImageBitmap(window);
		ibs.add(ib);
//		bmvs.add(new BitmapView(this, window, height, width));
		index++;
		
		if(elementType == EDITTEXT) {
//			bmvs.get(index).setFocusable(true);
//			bmvs.get(index).setFocusableInTouchMode(true);
//			bmvs.get(index).setOnKeyListener(new MyKeyListener());
			ibs.get(index).setFocusable(true);
			ibs.get(index).setFocusableInTouchMode(true);
			ibs.get(index).setOnKeyListener(new MyKeyListener());
		}
				
		if(elementType == BUTTON || elementType == EDITTEXT) {
//			bmvs.get(index).setOnClickListener(new MyClickListener(this, elementType));
			ibs.get(index).setOnTouchListener(new MyTouchListener(this, elementType));
		}
		


//		elements.put(buttonVals, bmvs.get(index));
		elements.put(buttonVals, ibs.get(index));

		RelativeLayout ll = (RelativeLayout)findViewById(R.id.app_view_layout);
		LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);//new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);

		lp.setMargins(x, y, x + width, y + height);
//		ll.addView(bmvs.get(index), lp);
		ll.addView(ibs.get(index), lp);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.activity_app_view, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle item selection
		switch (item.getItemId()) {
		case R.id.quit:
			// TODO Stop connection thread
			stopThreads = true;
			
			// Close RDP connections
		case android.R.id.home:
			// TODO Stop connection thread
			stopThreads = true;
//			RelativeLayout ll = (RelativeLayout)findViewById(R.id.app_view_layout);
			Intent i = new Intent(getApplicationContext(), LinkFileListActivity.class);
			i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
			startActivity(i);
			return true;
		default:
			return super.onOptionsItemSelected(item);
		}
	}

	private ArrayList<int[]> overlap() {
		int[] update = vnc.updatedCoordinates();
		int xmin = update[0];
		int ymin = update[1];
		int xmax = xmin + update[2];
		int ymax = ymin + update[3];
		
		ArrayList<int[]> updatedElements = new ArrayList<int[]>();

		for (int[] c: elements.keySet()) {
			int uxmin = c[4];
			int uymin = c[5];
			int uxmax = xmin + c[6];
			int uymax = ymin + c[7];

			//check overlap
			//TODO Test this
			if(xmin <= uxmax && ymin <= uymax && xmax >= uxmin && ymax >= uymin) {
				//return coordinates of overlapped element
				updatedElements.add(c);
			}
		}
		
		//if no overlap
		return updatedElements;
	}
	
	/**
	 * @author bweintra
	 *
	 */
	private class VNCThread extends Thread {
		@Override 
		public void run(){
			try{
				vnc = new VNC(host, port, username, password);
				//attempt to get first frame for desktop
				vnc.connectToVNC();
				if(vnc.connectToFTP()){
    				Log.d("msg", "ftp connection successful");
				}
				
				BufferedReader br = vnc.downloadFTPFile(appName);
				
				xml = "";
				String line = "";
				
				if (br.ready()) {
					while (true) {
						try {
							line = br.readLine();
						} catch (Exception e) {
							Log.d("msg", e.toString());
						}
						if (line == null) break;
						xml += line;
					}
					br.close();
					
				}
				
				vnc.closeFTPCConnection();
				
				mutexVNC.release();
			
				vnc.processNormalProtocol();
				
			} catch(Exception e2){
				Log.d("msg","this " + e2.toString());
			}
		}
	}
	
	/**
	 * @author bweintra
	 *
	 */
	private class BitmapLoop extends Thread {
		@Override 
		public void run(){
			while(true){
				if(vnc.hasUpdated()){
					master = vnc.getBitmap();
					vnc.ackUpdate();
					mutexVNC.release();
					break;
				}
			}
			
			while (!stopThreads) {
				if(vnc.hasUpdated()){

					//ArrayList<int[]> updatedElements = overlap();
					//if(!updatedElements.isEmpty()) {
					master = vnc.getBitmap();
					for(int[] c: elements.keySet()){

//						BitmapView b = elements.get(c);
						MyImageButton ib = elements.get(c);


						Bitmap temp = Bitmap.createBitmap(master, c[4], c[5], c[6], c[7]);
						//BitmapView newBmv = new BitmapView(getApplicationContext(), master, c[2], c[3]);
						
//						b.setBitmap(temp);
						ib.setBitmap(temp);
					}
					//}
					
					vnc.ackUpdate();
				}
			}
		}
	}
	
	private class MyClickListener implements View.OnClickListener {
		private int elementType;
		
		public MyClickListener(Context context, int et) {
			super();
			elementType = et;
		}
		
		public void onClick(View v) {
			for(int[] c: elements.keySet()) {
				if(c[0] == v.getX() && c[1] == v.getY()) {
					int middleX = c[4] + c[6]/2;
					int middlyY = c[5] + c[7]/2;
					Log.d("msg", "x: " + middleX + " y: " + middlyY);
					try {
						vnc.sendMouseEvent(middleX, middlyY, 0, 1);
						vnc.sendMouseEvent(middleX, middlyY, 0, 0);

						// Manually show soft keyboard
						if(elementType == EDITTEXT) {
							InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
						    if (inputMethodManager != null) {
						        inputMethodManager.showSoftInput(v, 0);
						    }
						}

					} catch (IOException e1) {
						e1.printStackTrace();
					}

				}
			}

			
	
		}	
	}
	
	private class MyTouchListener implements View.OnTouchListener {
		private int elementType;
		
		public MyTouchListener(Context context, int et) {
			super();
			elementType = et;
		}
		
		@Override
		public boolean onTouch(View v, MotionEvent me) {
			if(me.getAction() == MotionEvent.ACTION_DOWN) {
				for(int[] c: elements.keySet()) {
					if(c[0] == v.getX() && c[1] == v.getY()) {
//						{x, y, width, height, sx, sy, sWidth, sHeight};
						
//						int x = (int) (c[4] + me.getX());//c[6]/2;
//						int y = (int) (c[5] + me.getY());//c[7]/2;
						int x = c[4] + c[6]/2;
						int y = c[5] + c[7]/2;
						Log.d("msg", "x: " + x + " y: " + y);
						try {
							vnc.sendMouseEvent(x, y, 0, 1);
							vnc.sendMouseEvent(x, y, 0, 0);

							// Manually show soft keyboard
							if(elementType == EDITTEXT) {
								InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
								if (inputMethodManager != null) {
									inputMethodManager.showSoftInput(v, 0);
								}
							}

						} catch (IOException e1) {
							e1.printStackTrace();
						}

					}
				}

			}
			return true;
		}	
	}
	
	private class MyKeyListener implements View.OnKeyListener {

		@Override
		public boolean onKey(View v, int keyCode, KeyEvent event) {
			if (event.getAction() != KeyEvent.ACTION_DOWN) {
				
				//Send input text
				try {
					vnc.keyEvent(keyCode, event, true);
				} catch (IOException e) {
					Log.d("msg",e.toString());
				}
			}

			return true;
		}
	}
	
}
